import bpy
from os.path import splitext
from bpy.types import Operator
from bpy.props import StringProperty
from ...addon.paths import FluidLabPaths
from ...addon.naming import FluidLabNaming
from ...libs.functions.libraries import append_from_library
from ...libs.functions.get_common_vars import get_common_vars
from ...libs.materials.db_control import build_in_mat_lib
from ...libs.functions.nodes import get_node_index_or_identifier_by
from ...libs.functions.geometry_nodes import set_exposed_attributes_of_gn, add_node, connect_nodes, get_node_index_or_identifier_by



class FLUIDLAB_OT_shading_tmbs_arrows(Operator):
    bl_idname = "fluidlab.shading_tmbs_arrows"
    bl_label = "Shading Thumbnails Arrows"
    bl_description = "Shading Thumbnails Arrows"
    bl_options = {"REGISTER", "UNDO"}

    direction: StringProperty(default="")

    
    def execute(self, context):

        fluidlab = get_common_vars(context, get_fluidlab=True)
        
        # Si tenemos activo algún Thumb:
        if fluidlab.shader_props.thumbnails:
            
            # El thumb name actual:
            current_thumb = fluidlab.shader_props.thumbnails

            # Obtengo todos los nombres de los thumbs disponibles:
            avalidable_thumbs = [ei[0] for ei in fluidlab.shader_props.enum_items]

            # Haciendo que el index funcione de manera circular o ciclica sin out of ranges:
            curr_index = avalidable_thumbs.index(current_thumb)
            new_index = curr_index-1 if self.direction == 'LEFT' else curr_index+1 # <- establezco la dirección
            new_index = 0 if new_index > len(avalidable_thumbs)-1 else new_index # <- si es mayor que el maximo, clampeo

            # Seteo el nuevo activo:
            fluidlab.shader_props.thumbnails = avalidable_thumbs[new_index]

        return {'FINISHED'}

class FLUIDLAB_OT_shading_add_material(Operator):
    bl_idname = "fluidlab.shading_add_material"
    bl_label = "Add material"
    bl_description = "Add material to selected object"
    bl_options = {"REGISTER", "UNDO"}

    
    def execute(self, context):

        # selected_objects = [ob for ob in context.selected_objects if ob.type == 'MESH']
        # if not selected_objects:
        #     self.report({'ERROR'}, "Object selection is not valid!")
        #     return {'CANCELLED'}

        # NOTA: El CUSTOM necesita menos cosas por eso hay cosas q solo se ejecutan si no es custom.

        fluid_mesh, shader_props = get_common_vars(context, get_fluid_mesh=True, get_shader_props=True)

        if shader_props.material_types != 'CUSTOM':

            active_thumb, extension = splitext(shader_props.thumbnails) # <-- Le quito la extension al nombre
            # print(active_thumb, build_in_mat_lib)

            total = len([ng for ng in bpy.data.materials if ng.name.startswith(active_thumb)])
            
            padding = len(str(total))+1
            new_name = active_thumb + "_" + str(total).zfill(padding)

            blend_file = build_in_mat_lib[0][1]

            # imporatamos el material:
            material_name = next((data[2] for data in build_in_mat_lib if data[0] == "mat" and data[1] == active_thumb), None)
            # print(FluidLabPaths.LIBS_MATERIALS_PATH, blend_file, material_name)

            material_type = "GRAIN" if material_name.endswith("_Grain") else "FLUID"

            append_from_library(
                context,
                FluidLabPaths.LIBS_MATERIALS_PATH,
                "Material",
                blend_file,
                material_name,
                only_if_not_exist=True
            )
            
        mat = shader_props.material_pointer if shader_props.material_types == 'CUSTOM' else bpy.data.materials.get(material_name)
        if mat:

            if shader_props.material_types != 'CUSTOM':
                mat.name = new_name
                # Lo marcamos como material nuestro:
                mat[FluidLabNaming.FLUIDLAB_MAT] = True
            
            active_mesh_item = fluid_mesh.active
            ob = active_mesh_item.ob

            if shader_props.material_types != 'CUSTOM':

                #-----------------------------------------------------------------------------------------
                # Para el MixColors creamos nodos extra:
                #-----------------------------------------------------------------------------------------
                if material_name == "FL_MixColors":
                    node_tree = mat.node_tree
                    nodes = node_tree.nodes
                    main_principled = nodes.get('main_settings')
                    if main_principled:
                        attr_node = add_node(node_tree, (main_principled.location.x-300, main_principled.location.y), 'ShaderNodeAttribute', False)
                        attr_node.attribute_name = "fl_color"
                        attr_node.select = False
                        connect_nodes(node_tree, attr_node, "Color", main_principled, "Base Color", False)
                
                #-----------------------------------------------------------------------------------------

            # Guardo los materiales que no son nuestros previamente:
            rest_of_mats = [mat_slot.name for mat_slot in ob.material_slots if mat_slot.material is not None and FluidLabNaming.FLUIDLAB_MAT not in mat_slot.material and mat_slot.material != mat]
            
            # Elimino nuestros materiales previos (limpieza):
            my_mats_names = [mat_slot.name for mat_slot in ob.material_slots if mat_slot.material is not None and FluidLabNaming.FLUIDLAB_MAT in mat_slot.material and mat_slot.material != mat]
            [ob.data.materials.pop(index=ob.material_slots.find(mat_name)) for mat_name in my_mats_names if ob.material_slots.find(mat_name) != -1]

            gn_mod = ob.modifiers.get(FluidLabNaming.GN_MESH_MOD)
            if gn_mod:
    
                group_input = next((node for node in gn_mod.node_group.nodes if node.name.startswith("Group Input")), None)
                if group_input:

                    # Seteo su nuevo material elegido por el usuario en el GN: 
                    if shader_props.material_types == 'CUSTOM':
                        #(si es custom lo seteo tanto para grain como para fluid):
                        set_exposed_attributes_of_gn(gn_mod, "Material", mat, debug=False)
                        set_exposed_attributes_of_gn(gn_mod, "Grain Material", mat, debug=False)
                    else:
                        # Si no es custom cada uno para su sitio:
                        gn_target_input_name_prop = "Grain Material" if material_type == 'GRAIN' else "Material"
                        set_exposed_attributes_of_gn(gn_mod, gn_target_input_name_prop, mat, debug=False)

                    # Obtengo los materiales seteados en el GN (para luego que cohincida el material slots con lo q hay en el GN):
                    id_mat_fluid = get_node_index_or_identifier_by("identifier", "name", "outputs", group_input, "Material", debug=False)
                    fluid_mat = gn_mod[id_mat_fluid]

                    id_mat_grain = get_node_index_or_identifier_by("identifier", "name", "outputs", group_input, "Grain Material", debug=False)
                    grain_mat = gn_mod[id_mat_grain]
                    
                    # Vuelvo a traer los materiales a material slots en su orden si estan seteados en el GN:

                    # Si estoy en modo custom borro si ya existian asignados estos materiales:
                    if shader_props.material_types == 'CUSTOM':
                        for slot_index in reversed(range(len(ob.material_slots))):
                            material_slot = ob.material_slots[slot_index]
                            if material_slot.material == mat:
                                ob.data.materials.pop(index=slot_index)
                    
                    # Agrego los nuevos materiales "fluid_mat" y "grain_mat" (recuperados del GN) y si son de tipo FLUID o GRAIN, solo son agregos 
                    # al material slots y al listado main_mats, si no estuvieran ya previamente agregados. 
                    # Si es CUSTOM si permito agregarlos mas de una vez ya que va en ambos slots el mismo material.
                    def add_mats(list_mats, control_mat):
                        if control_mat:
                            if shader_props.material_types != 'CUSTOM':
                                if control_mat.name not in ob.data.materials: 
                                    ob.data.materials.append(control_mat)
                                    list_mats.append(control_mat.name)
                            else:
                                ob.data.materials.append(control_mat)
                                list_mats.append(control_mat.name)

                        return list_mats
                    
                    main_mats = []
                    main_mats = add_mats(main_mats, fluid_mat)
                    main_mats = add_mats(main_mats, grain_mat)

                    #-----------------------------------------------------------------------------------------------------------
                    # Reordenar los materiales (Si el usuario tuviera otros materiales, por eso nos aseguramos de reordenarlos)
                    # Los fluid mat en el slot 0 siempre y los grain mat en el slot 1 siempre:
                    #-----------------------------------------------------------------------------------------------------------
                    new_order = main_mats+rest_of_mats
                    # print(f"new order: {new_order}")

                    # Reasignar los materiales al objeto en el nuevo orden:
                    for mat_slot, name in zip(ob.material_slots, new_order):
                        
                        # Si el material ya es el mismo skipeamos:
                        if mat_slot.material.name == name:
                            continue

                        target_mat = bpy.data.materials.get(name)
                        if target_mat:
                            mat_slot.material = target_mat
                    #-----------------------------------------------------------------------------------------------------------
                    
                    # Lo hacemos el material activo:
                    if shader_props.material_types != 'CUSTOM':
                        target_name = grain_mat.name if material_type == 'GRAIN' else fluid_mat.name
                    else:
                        target_name = fluid_mat.name
                    
                    ob.active_material_index = ob.material_slots.find(target_name)
                    # print(target_name)

        # for ob in selected_objects:
        #     ob.select_set(True)
        shader_props.vec_color_shading = shader_props.get_default_properties("vec_color_shading") 
        shader_props.shader_subsectioins = 'MAT_SETTINGS'
        return {'FINISHED'}
